/***************************************************************************
 *   Copyright (C) 2008/2009 by Philipp Müller                             *
 *   philipp.mueller@gmx.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA          *
 ***************************************************************************/

#include <QtDebug>
#include <QCoreApplication>
#include <QPainter>

#include "channellistmodel.h"
#include "qtvomp.h"


ChannelListModel::ChannelListModel( MainWindow * mainWindow, QObject *parent ) : QAbstractListModel( parent ),
    m_mainWindow( mainWindow )
{
}


int ChannelListModel::rowCount( const QModelIndex &/*parent*/ ) const
{
    return m_mainWindow->channelList()->count();
}


int ChannelListModel::columnCount( const QModelIndex &/*parent*/ ) const
{
    return ChannelListModel::ChannelColumnsLast;
}


QVariant ChannelListModel::data( const QModelIndex &index, int role ) const
{
    if ( !index.isValid() )
        return QVariant();

    if ( index.row() >= m_mainWindow->channelList()->size() )
        return QVariant();

    switch ( role )
    {
        case Qt::EditRole: //Requested from Live-TV epg through QDataWidgetMapper
        {
            if ( index.column() == ChannelListModel::ChannelName )
            {
                return m_mainWindow->channelList()->at( index.row() ).name;
            }

            if ( index.column() == ChannelListModel::ChannelNumber )
            {
                return m_mainWindow->channelList()->at( index.row() ).channel;
            }

            if ( index.column() == ChannelListModel::ChannelIcon )
            {
                QString path = QCoreApplication::applicationDirPath() +
                               "/images/logos/" +
                               m_mainWindow->channelList()->at( index.row() ).name + ".png";
                return QPixmap( path );
            }

            if ( index.column() == ChannelListModel::EpgTime )
            {
                if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                {
                    return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time.toString( "hh:mm" );
                }
                else
                {
                    return QVariant();
                }
            }

            if ( index.column() == ChannelListModel::EpgTitle )
            {
                updateEpgStatus( index.row() );

                if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                {
                    return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].title;
                }
                else
                {
                    return QVariant();
                }
            }

            return QVariant();
        }

        case Qt::DisplayRole:
        case ChannelListModel::ChannelNameRole:
        {
            return m_mainWindow->channelList()->at( index.row() ).name;
        }

        case Qt::ToolTipRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                if( m_mainWindow->centralWidget()->currentIndex() != MainWindow::WIDGET_CHANNELS )
                {
                    //TODO show the tooltip in the epg view as well
                    return QVariant();
                }

                QString tip = m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].description;

                if ( tip.isEmpty() )
                {
                    return QString( tr( "No description available" ) );
                }

                //We want max 80 Characters per line. Otherwise it is getting too wide.
                //We check first for ": " then for ". ", etc for a line break
                //TODO Move this into a global function
                static const int maxChars = 80;
                tip.remove( "\r" ); //No MS returns

                QStringList tipList = tip.split( "\n" );
                int round = 0; //Just safety for endless loops
                int found;

                for ( int i = 0; i < tipList.size(); ++i )
                {
                    while ( ( tipList[ i ].length() > maxChars ) && ( round < 500 ) )
                    {
                        //Check for ":"
                        found = tipList[ i ].lastIndexOf( ": ", maxChars );
                        if ( found > maxChars / 3 )
                        {
                            tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
                            tipList[ i ] = tipList[ i ].left ( found + 1 );
                        }
                        else
                        {
                            //Check for "?"
                            found = tipList[ i ].lastIndexOf( "? ", maxChars );
                            if ( found > maxChars / 3 )
                            {
                                tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
                                tipList[ i ] = tipList[ i ].left ( found + 1 );
                            }
                            else
                            {
                                //Check for "!"
                                found = tipList[ i ].lastIndexOf( "! ", maxChars );
                                if ( found > maxChars / 2 )
                                {
                                    tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
                                    tipList[ i ] = tipList[ i ].left ( found + 1 );
                                }
                                else
                                {
                                    //Check for "."
                                    found = tipList[ i ].lastIndexOf( ". ", maxChars );
                                    //We may have something like a data, so ignore it if the character before is a number.
                                    if ( ( found > 0 ) &&
                                        ( tipList[ i ].at( found - 1 ) >= QChar('0') ) &&
                                        ( tipList[ i ].at( found - 1 ) <= QChar('9') ) )
                                    {
                                        found = 0;
                                    }

                                    if ( found > maxChars / 2 )
                                    {
                                        tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
                                        tipList[ i ] = tipList[ i ].left ( found + 1 );
                                    }
                                    else
                                    {
                                        //Check for " "
                                        found = tipList[ i ].lastIndexOf( " ", maxChars );
                                        if ( found > 0 )
                                        {
                                            tipList.insert( i + 1, tipList[ i ].mid( found + 1 ) );
                                            tipList[ i ] = tipList[ i ].left ( found );
                                        }
                                        else
                                        {
                                            //Default: We need to cut in the middle of a word
                                            found = maxChars;
                                            if ( found < tipList[ i ].length() )
                                            {
                                                tipList.insert( i + 1, tipList[ i ].mid( found + 1 ) );
                                                tipList[ i ] = tipList[ i ].left ( found );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        round++;
                        if ( round == 500 )
                        {
                            qWarning() << "ChannelListModel::data: endless loop in wordwrap calculation of tooltip";
                        }
                    }
                }

                tip = tipList.join( "\n" );
                return tip;
            }
            else
            {
                return QVariant();
            }
        }

        case ChannelListModel::ChannelNumberRole:
        {
            return m_mainWindow->channelList()->at( index.row() ).channel;
        }

        case ChannelEpgTriggerUpdate:
        {
            updateEpgStatus( index.row() );
            return QVariant();
        }

        case ChannelListModel::ChannelEpgTitleRole:
        {
            updateEpgStatus( index.row() );

            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].title;
            }
            else
            {
                return QVariant();
            }
        }

        case ChannelListModel::ChannelEpgSubTitleRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].subTitle;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgDescriptionRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].description;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeFromToRole:
        {
            return timeFromTo( index.row(), 0 );
        }

        case ChannelListModel::ChannelEpgDurationRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].duration;
            else
                return QVariant();
        }

        case Qt::DecorationRole:
        {
            QString path = QCoreApplication::applicationDirPath() +
                           "/images/logos/" +
                           m_mainWindow->channelList()->at( index.row() ).name + ".png";
            return QVariant( QIcon( path ) );
        }

        default:
        {
            return QVariant();
        }
    }
}


bool ChannelListModel::insertRows( int position, int rows, const QModelIndex &/*parent*/ )
{
    beginInsertRows( QModelIndex(), position, position + rows - 1 );

    for ( int row = 0; row < rows; ++row )
    {
        ChannelEntry entry;
        m_mainWindow->channelList()->insert( position, entry );
    }

    endInsertRows();

    return true;
}


bool ChannelListModel::removeRows( int position, int rows, const QModelIndex &/*parent*/ )
{
    if ( rows == 0 )
    {
        return false;
    }

    beginRemoveRows( QModelIndex(), position, position + rows - 1 );

    for ( int row = 0; row < rows; ++row )
    {
        m_mainWindow->channelList()->removeAt( position );
    }

    endRemoveRows();

    return true;
}


void ChannelListModel::epgDataChanged( int row )
{
    if ( row < 0 )
    {
        emit layoutChanged();
    }
    else
    {
        emit dataChanged( index( row, 0 ), index( row, 0 ) );
    }
}


void ChannelListModel::updateEpgStatus( int row ) const
{
    bool updateEpg = false;
    quint32 duration = 60 * 60 * 24 * 8; // get 8 days into the future

    if ( m_mainWindow->channelList()->at( row ).scheduleListLastUpdate < m_mainWindow->serverTime() )
    {
        updateEpg = true;
    }

    if ( m_mainWindow->channelList()->at( row ).scheduleList.size() > 0 )
    {
        if ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].duration > 0 )
        {
            duration = ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].duration + 1 ) * 60;

            if ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].time.addSecs( duration ) < m_mainWindow->serverTime() )
            {
                updateEpg = true;
            }
        }
    }

    if ( updateEpg )
    {
//         qDebug() << "ChannelListModel::updateEpgStatus with row = " << row;
        ( *m_mainWindow->channelList() )[ row ].scheduleListLastUpdate = m_mainWindow->serverTime().addSecs( 300 );

        emit getSchedule( ( *m_mainWindow->channelList() )[ row ].channel, m_mainWindow->serverTime(), duration );
    }
}


void ChannelListModel::updateSchedule( quint32 channel, const ScheduleList & scheduleList )
{
//     qDebug() << "ChannelListModel:updateSchedule: size = " << schedules.size() << " and channel" << channel;

    int row = m_mainWindow->channelList()->rowFromChannel( channel );
    ( *m_mainWindow->channelList() )[ row ].scheduleList.clear();

    for ( int i = 0; i < scheduleList.size(); ++i )
    {
        int row = m_mainWindow->channelList()->rowFromChannel( channel );

        if ( row > m_mainWindow->channelList()->count() - 1 || row < 0 )
        {
            qWarning() << "ChannelListModel::setSchedule: row is out of range: row=" << row;
            return;
        }

        ( *m_mainWindow->channelList() )[ row ].scheduleList.append( scheduleList[ i ] );
    }

    emit dataChanged( index( row, 0 ), index( row, 0 ) );

    //Do we need to update epgChannel?
    QModelIndexList selectionList = m_mainWindow->channelListSelectionModel()->selectedIndexes();
    if ( selectionList.size() != ChannelListModel::ChannelColumnsLast )
    {
        return;
    }

    if ( m_mainWindow->channelListSelectionModel()->selectedIndexes()[ 0 ].row() == row )
    {
        emit epgListChanged( row );
    }
}


QString ChannelListModel::timeFromTo( int row, int item ) const
{
    if ( row < m_mainWindow->channelList()->size() )
    {
        if ( item < m_mainWindow->channelList()->at( row ).scheduleList.size() )
        {
            QDateTime epgTime      = m_mainWindow->channelList()->at( row ).scheduleList[ item ].time;
            quint32 epgDuration    = m_mainWindow->channelList()->at( row ).scheduleList[ item ].duration;

            if ( epgDuration > 0 )
            {
                return epgTime.toString( "hh:mm" ) + " - " + epgTime.addSecs( epgDuration ).toString( "hh:mm" );
            }
        }
    }

    return QString();
}
